#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cbm.h>
#include <conio.h>

#include "Buffers.h"
#include "Trim.h"
#include "Splitter.h"
#include "Drives.h"
#include "Dispatcher.h"
#include "cbm_dir.h"

#pragma code-name("WRIMGCODE");
#pragma rodata-name("WRIMGCODE");

#define D64_SIZE  689
#define D71_SIZE 1377
#define D81_SIZE 3226

enum
{
	F_UNKNOWN,
	F_1541,						// single-sided
	F_1571,						// double-sided
	F_1581,
	F_2030,
	F_2040,
	F_8050,						// single-sided
	F_8250,						// double-sided
	F_D2M,
	F_DNP,
	F_FAT,
	F_IDE64
};

static const unsigned char l[] =	// sectors per track on 1541/1571 disks
// Note:  This table can handle both sides of a 1571 because the modulo
// operator effectively "unfolds" it into two copies.
{	21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
	19,19,19,19,19,19,19,
	18,18,18,18,18,18,
	17,17,17,17,17
};

// Detect the format of the disk in the named drive.
//
static unsigned char __fastcall__ getFormat(unsigned char drive)
{
	int r;
	unsigned char format = F_UNKNOWN;

	// Decode the drive's reset-message.
	memset(buffer1, 0, sizeof(buffer1));
	cbm_open(15, drive, 15, "ui");
	if ((r = cbm_read(15, buffer1, sizeof(buffer1))) > 0)
	{
		buffer1[r] = '\0';
		if (strstr(buffer1,"1541") != NULL ||
			strstr(buffer1,"1540") != NULL ||
			strstr(buffer1,"1570") != NULL)
		{
			format = F_1541;
		}
		else if(strstr(buffer1,"1581") != NULL)
		{
			format = F_1581;
		}
		else if(strstr(buffer1,"ide64") != NULL)
		{
			format = F_IDE64;
		}
		else
		{
			// Either the message doesn't tell the format, or it's ambiguous.
			// Look at the format-codes at the beginning of the raw directory
			// file.
			// (Actually, this method could be used instead of the
			// reset-message, for almost all formats.)
			cbm_open(2, drive, 2, "$");
			if ((r = cbm_read(2, buffer1, 2)) > 0)
			{
				buffer1[r] = '\0';
				switch (buffer1[0])
				{
				case 'a':
					// See if it's double- or single-sided.
					format = ((signed char)buffer1[1] < 0) ? F_1571 : F_1541;
					break;
				case 'd':
					format = F_1581;
					break;
				case 'h':
					format = F_DNP;
					break;
//#ifdef __C64__
//				case 'i':
//					format = F_IDE64;
//					break;
//#endif
				}
			}
			cbm_close(2);
		}
	}
	cbm_close(15);

	if (r <= 0)
	{
		printf("\nError getting format [%d]", r);
	}

	return format;
}

unsigned char writeImage(unsigned char driveIndex)
{
	int r = 0, x = 0, y = wherey() + 1;
	unsigned char result = 0;
	drive_t *drive = &drives[driveIndex];
	tokenCollection_t *collection = NULL;
	parsedPath_t *spath = NULL;
	parsedPath_t *tpath = NULL;
	static char buffer[256];
	char *text = NULL;
	unsigned char sd, td, i, j, sectorsThisTrack;
	unsigned char tracks, sectors;
	bool isD64 = true, isD71 = false;
	unsigned int p = 0, size = D64_SIZE;
	struct cbm_dirent entry;

	if(0) cgetc();

	strcpy(buffer, buffer3);
	collection = splitString(buffer);
	text = collection->tokens[1].text;

	if(collection->count != 3)
	{
		printf("\nusage: wrimg source target\n");
		free(collection);
		return 0;
	}

	spath = parsePath(1, collection, driveIndex);

	driveIndex = getDrive(spath->device);
	if(driveIndex == 0xFF) 
	{
		printf("\nUnknown source device.");
		free(collection);
		free(spath);
		return 1;
	}

	tpath = parsePath(2, collection, driveIndex);

	if(getDrive(tpath->device) == 0xFF) 
	{
		printf("\nUnknown target device.");
		free(collection);
		free(spath);
		free(tpath);
		return 2;
	}

	drive = &drives[getDrive(tpath->device)];

	if(drive->type != CBM15x1)
	{
		printf("\nTarget can only be 15x1 type drive.");
		free(collection);
		free(spath);
		return 4;
	}

	if(strlen(tpath->file) > 0)
	{
		printf("\nTarget must specify a drive only.");
		free(collection);
		free(spath);
		free(tpath);
		return 3;
	}

	if(strlen(spath->file) == 0)
	{
		printf("\nSource must specify a file.");
		free(collection);
		free(spath);
		free(tpath);
		return 5;
	}

	if (
		//targetPanel->drive != NULL &&
		(td = tpath->device) !=
		(sd = spath->device))
	{
		if(drives[driveIndex].type != CBM15x1)
		{
			memset(buffer1, 0, sizeof(buffer1));
			sprintf(buffer1, "cd%s", spath->path);
			if((r = cbm_open(15, spath->device, 15, buffer1)) != 0)
			{
				printf("\nInvalid source path.");
				cbm_close(15);
				free(collection);
				free(spath);
				free(tpath);
				return 1;
			}
			cbm_close(15);
		}

		if((r = cbm_opendir2(10, spath->device, "$")) == 0)
		{
			do
			{
				memset(entry.name, 0, sizeof(entry.name));
				r = cbm_readdir(10, &entry);
			} while (r == 0 && strcmp(entry.name, spath->file) != 0);

			cbm_close(10);

			if(r != 0)
			{
				printf("\nSource file not found.");
				free(collection);
				free(spath);
				free(tpath);
				return 1;
			}
		}
		else
		{
			printf("\nCould not open source directory.");
			cbm_close(10);
			free(collection);
			free(spath);
			free(tpath);
			return 1;
		}

		if(entry.name[0] != 0)
		{
			switch (entry.size)
			{
			case D64_SIZE:
			case D71_SIZE:
			case D81_SIZE:
				break;
			default:
				printf("\nNot a supported image.");
				free(collection);
				free(spath);
				free(tpath);
				return 1;
			}

			//saveScreen();
			printf("\nHit a key to continue, STOP to abort.");
			if(cgetc() != CH_STOP)
			{
				//cbm_open(15, sd, 15, "");
				if((r = cbm_open(2, sd, 2, spath->file)) == 0)
				{
					/*
					100 OPEN 1,8,15
					110 OPEN 2,8,2, "#"
					120 PRINT#2, "TEST DATA"
					130 PRINT#1, "U2 2 0 10 0"
					140 CLOSE 2 : CLOSE 1
					*/

					printf("\nFormatting disk...");
					//cbm_open(14, td, 15, "n:temp,00");
					if ((r = cbm_open(3, td, 15, "n:temp,00")) == 0)
					{
						cbm_close(3); 
						cbm_open(3,td,3,"#");
						cbm_open(14,td,15,"");

						printf("\nMaking disk...");

						x = 0;
						y = wherey();
						if(y > 24)
						{
							scrolldown();
							scrolldown();
							y = 25;
						}

						switch(entry.size)
						{
						  case D64_SIZE:
							tracks = 35u;
							break;
						  case D71_SIZE:
							tracks = 35u*2;
							break;
						  case D81_SIZE:
							sectors = 40u;
							tracks = 80u;
							break;
						}

						//waitForEnterEscf("size: %4u, tracks: %2u", currentNode->size, tracks);

						for(i=0;i<tracks;++i)
						{
							switch(entry.size)
							{
							  case D64_SIZE:
							  case D71_SIZE:
								sectors = l[i%35];
								break;
							}
							for(j=0;j<sectors;++j)
							{
								if(kbhit() && cgetc() == CH_STOP)
								{
									// Break out of the outer loop by presetting
									// the track number beyond the highest
									// supported value.
									i=77*2;
									break;
								}

								memset(buffer3, 0, 0x100);
								r = cbm_read(2,buffer3,0x100);
								// XXX: Check that r==256.

								//memcpy(temp, (fileBuffer + 1), 256-1);
								//temp[256-1] = fileBuffer[0];
								//
								//cbm_write(3,temp,256);
								cbm_write(14, "b-p 3 0", 7);
								cbm_write(3,buffer3,0x100);

								memset(buffer1, 0, sizeof(buffer1));
								cbm_write(14,buffer1,
									sprintf(buffer1, "u2 3 0 %u %u", i+1, j));

								memset(buffer1, 0, sizeof(buffer1));
								sprintf(buffer1, "%s t:%02d s:%02d", spath->file, i+1, j);
								cputsxy(x, y, buffer1);
							}
						}
						cbm_close(2);
						cbm_close(3);
						cbm_close(14);
						cbm_close(15);
					}
					else
					{
						printf("\nTarget: %s",
							r >= 0 ? "unit not there" : buffer);
						cbm_close(3); cbm_close(14);
						//waitForEnterEsc();
					}
				}
				else
				{
					printf("\nSource: %s",
						r >= 0 ? "unit not there" : buffer);
					//waitForEnterEsc();
				}
				cbm_close(2);
				cbm_close(15);
			}
			else
			{
				printf("\nGot here -b-.");
				r = 12;
			}
		}
		else
		{
			printf("\nGot here -a-.");
			r = 11;
		}
	}
	else
	{
		printf("\nSource and target devices cannot be the same.");
		r = 10;
	}

	cbm_close(2); cbm_close(3);
	cbm_close(15); cbm_close(14);
	free(collection);
	free(spath);
	free(tpath);
	return r;
}
